#pragma once
#include "Connection.hpp"
#include "Calculator.hpp"

namespace wxd
{
    class IOService : public Connection
    {
    public:
        IOService(int fd)
        {
            // 初始化sockfd
            SetSockfd(fd);
            // 设置关心事件
            SetEvents(EPOLLIN | EPOLLET);
            // 设置非阻塞
            SetNonBlock(fd);
        }

        virtual void Recv() override
        {
            // 读取-ET模式，要确保读完本轮-非阻塞循环读取
            while (true)
            {
                char buffer[1024];
                LOG(LogLevel::DEBUG) << "reading fd = " << _sockfd;
                int n = recv(_sockfd, buffer, sizeof(buffer), 0);
                if (n > 0)
                {
                    buffer[n] = 0;
                    // 添加到缓冲区
                    AppendInbuffer(buffer);
                }
                else if (n == 0)
                {
                    // 本轮已经读完
                    LOG(LogLevel::DEBUG) << "对端关闭连接 fd = " << Sockfd();
                    Except();
                    return;
                }
                else
                {
                    if (errno == EAGAIN)
                    {
                        // 本轮读完
                        LOG(LogLevel::DEBUG) << "1";
                        break;
                    }
                    else if (errno == EINTR) // 被信号打断
                    {
                        LOG(LogLevel::WARNING) << "读被信号打断，继续...";
                        continue;
                    }
                    else
                    {
                        // 出错了
                        // 调用异常
                        LOG(LogLevel::DEBUG) << "1" << _sockfd;

                        Except();
                    }
                }
            }
            // 读到这里本轮读完
            LOG(LogLevel::DEBUG) << "发送缓冲区：" << GetInbuffer();
            // 处理报文，将响应放入写缓冲区
            std::string resp_str = Parse(GetInbuffer());
            if (!resp_str.empty())
            {
                AppendOutBuffer(resp_str);
            }
            // 方法一，直接调用写方法
            // Send();
            // 方法二，设置关心写事件，然后Reactor就会自动调用写方法
            _owner->ModifyConnection(Sockfd(),true,true,true);
        }
        virtual void Send() override
        {

            while (true)
            {
                // 直接send
                LOG(LogLevel::DEBUG) << "outbuffer:" << _outbuffer;
                int n = send(_sockfd, GetOutBuffer().c_str(), GetOutBuffer().size(), 0);
                if (n > 0)
                {
                    // 发送成功 - 返回实际读到的字节
                    // 从发送缓冲区中除去发送成功的
                    GetOutBuffer().erase(0, n);
                }
                else if (n == 0)
                {
                    // 缓冲区数据已经全部发送
                    break;
                }
                else
                {
                    if (errno == EAGAIN)
                    {
                        // 发送缓冲区已经满了
                        break;
                    }
                    else if (errno == EINTR)
                    {
                        continue;
                    }
                    else
                    {
                        // 发送失败
                        Except();
                    }
                }
            }

            if (!GetOutBuffer().empty())
            {
                // 发送缓冲区已满，写条件不就绪，设置关心写条件
                _owner->ModifyConnection(Sockfd(),true,true,true);
            }
            else
            {
                // 写条件已经默认就绪，重置回只关注读条件
                _owner->ModifyConnection(Sockfd(),true,false,true);
            }
        }
        virtual void Except() override
        {
            // 处理异常-删除对应的连接
            LOG(LogLevel::DEBUG) << "客户端连接可能已经结束，关闭连接...";
            _owner->DelateConnection(Sockfd());
        }

    private:
    };
}